﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;

public class PngToIcoConverter
{
    /// <summary>
    /// 将 PNG 图片转换为 ICO 图标（支持多尺寸）
    /// </summary>
    /// <param name="inputPngPath">输入 PNG 文件路径</param>
    /// <param name="outputIcoPath">输出 ICO 文件路径</param>
    /// <param name="sizes">目标尺寸列表（如 16, 32, 48, 256）</param>
    public static void Convert(string inputPngPath, string outputIcoPath, IEnumerable<int> sizes)
    {
        if (!File.Exists(inputPngPath))
            throw new FileNotFoundException("输入 PNG 文件不存在", inputPngPath);

        if (!sizes.Any())
            throw new ArgumentException("目标尺寸列表不能为空", nameof(sizes));

        // 加载原始 PNG 图像
        using (var originalImage = Image.FromFile(inputPngPath))
        {
            // 生成所有尺寸的缩放图像及 PNG 数据
            var imageInfos = GenerateResizedImages(originalImage, sizes).ToList();

            // 构建 ICO 文件结构并写入
            WriteIcoFile(imageInfos, outputIcoPath);
        }
    }

    /// <summary>
    /// 生成指定尺寸的缩放图像（高质量）
    /// </summary>
    private static IEnumerable<(Bitmap Bmp, byte[] PngData)> GenerateResizedImages(Image original, IEnumerable<int> sizes)
    {
        foreach (var size in sizes.Distinct()) // 去重
        {
            if (size <= 0)
                throw new ArgumentException($"无效的尺寸：{size}", nameof(sizes));

            // 缩放图像（高质量插值）
            using (var resizedBmp = ResizeImage(original, size, size))
            {
                // 保存为 PNG 字节数组（保留透明通道）
                byte[] pngData;
                using (var ms = new MemoryStream())
                {
                    resizedBmp.Save(ms, ImageFormat.Png);
                    pngData = ms.ToArray();
                }
                yield return (resizedBmp, pngData);
            }
        }
    }

    /// <summary>
    /// 高质量缩放图像（双三次插值）
    /// </summary>
    private static Bitmap ResizeImage(Image image, int targetWidth, int targetHeight)
    {
        var bmp = new Bitmap(targetWidth, targetHeight);
        bmp.SetResolution(image.HorizontalResolution, image.VerticalResolution);

        using (var g = Graphics.FromImage(bmp))
        {
            g.CompositingMode = CompositingMode.SourceOver;
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic; // 高质量插值
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.PixelOffsetMode = PixelOffsetMode.HighQuality;

            // 绘制缩放后的图像（保持宽高比，避免拉伸）
            g.DrawImage(image, 0, 0, targetWidth, targetHeight);
        }
        return bmp;
    }

    /// <summary>
    /// 写入 ICO 文件（符合 Windows 图标格式规范）
    /// </summary>
    private static void WriteIcoFile(IEnumerable<(Bitmap Bmp, byte[] PngData)> imageInfos, string outputPath)
    {
        using (var fs = new FileStream(outputPath, FileMode.Create, FileAccess.Write))
        {
            // 1. 写入 ICO 文件头（6字节）
            ushort entryCount = (ushort)imageInfos.Count();
            byte[] header = new byte[6]
            {
                0x00, 0x00, // 保留字段（必须为0）
                0x01, 0x00, // 图标类型（1=ICO，2=CUR光标）
                (byte)(entryCount >> 0), (byte)(entryCount >> 8) // 目录项数量
            };
            fs.Write(header, 0, header.Length);

            // 2. 计算目录项和图像数据的偏移量
            uint dataOffset = 6 + (uint)(entryCount * 16); // 文件头6字节 + 所有目录项总大小（16字节/项）

            // 3. 写入目录项（每个16字节）和图像数据
            foreach (var info in imageInfos)
            {
                // 构建目录项元数据
                var entry = new IconDirectoryEntry
                {
                    Width = (byte)(info.Bmp.Width <= 255 ? info.Bmp.Width : 0), // 宽度（0=256）
                    Height = (byte)(info.Bmp.Height <= 255 ? info.Bmp.Height : 0), // 高度（0=256）
                    BitDepth = 32, // 32位色（支持PNG压缩）
                    DataSize = (uint)info.PngData.Length, // 图像数据大小（PNG字节流长度）
                    DataOffset = dataOffset // 图像数据在文件中的偏移量
                };

                // 写入目录项字节（16字节）
                byte[] entryBytes = new byte[16];
                entryBytes[0] = entry.Width;
                entryBytes[1] = entry.Height;
                entryBytes[2] = (byte)(entry.Reserved >> 0); // 保留字段（必须为0）
                entryBytes[3] = (byte)(entry.Reserved >> 8);
                entryBytes[4] = entry.BitDepth; // 位深（32）
                entryBytes[5] = 0; // 位深（高4位，32位时为0）
                Buffer.BlockCopy(BitConverter.GetBytes(entry.DataSize), 0, entryBytes, 10, 4); // 数据大小
                Buffer.BlockCopy(BitConverter.GetBytes(entry.DataOffset), 0, entryBytes, 14, 4); // 数据偏移

                fs.Write(entryBytes, 0, entryBytes.Length);

                // 写入图像数据（PNG字节流）
                fs.Write(info.PngData, 0, info.PngData.Length);

                // 更新下一个图像数据的偏移量
                dataOffset += (uint)info.PngData.Length;
            }
        }
    }

    /// <summary>
    /// ICO 目录项元数据（对应文件中的16字节结构）
    /// </summary>
    private struct IconDirectoryEntry
    {
        public byte Width;      // 宽度（像素，0=256）
        public byte Height;     // 高度（像素，0=256）
        public ushort Reserved; // 保留（必须为0）
        public byte BitDepth;   // 位深（32表示PNG压缩）
        public uint DataSize;   // 图像数据大小（字节）
        public uint DataOffset; // 图像数据在文件中的偏移量（字节）
    }
}